package org.liveboardgames.agricola.control.delegates.helper
{
	import mx.collections.ArrayCollection;
	import mx.collections.ListCollectionView;
	
	import org.liveboardgames.agricola.control.delegates.action.check.ActionCheck;
	import org.liveboardgames.agricola.control.delegates.action.overrideaction.ActionOverrider;
	import org.liveboardgames.agricola.control.delegates.mock.FarmFactory;
	import org.liveboardgames.agricola.model.enums.ActionTempoEnum;
	import org.liveboardgames.agricola.model.enums.AlternativeCostEnum;
	import org.liveboardgames.agricola.model.enums.BirthStatusEnum;
	import org.liveboardgames.agricola.model.enums.BoardTileTypeEnum;
	import org.liveboardgames.agricola.model.enums.DirectionEnum;
	import org.liveboardgames.agricola.model.enums.HouseTypeEnum;
	import org.liveboardgames.agricola.model.enums.ImprovementTypeEnum;
	import org.liveboardgames.agricola.model.enums.ResourceTypeEnum;
	import org.liveboardgames.agricola.vo.Game;
	import org.liveboardgames.agricola.vo.action.Action;
	import org.liveboardgames.agricola.vo.board.BoardTile;
	import org.liveboardgames.agricola.vo.card.ActionCard;
	import org.liveboardgames.agricola.vo.card.Card;
	import org.liveboardgames.agricola.vo.card.CookingPlace;
	import org.liveboardgames.agricola.vo.card.ImprovementCard;
	import org.liveboardgames.agricola.vo.card.Oven;
	import org.liveboardgames.agricola.vo.common.Peep;
	import org.liveboardgames.agricola.vo.farm.Farm;
	import org.liveboardgames.agricola.vo.farm.FarmElement;
	import org.liveboardgames.agricola.vo.farm.FarmTile;
	import org.liveboardgames.agricola.vo.farm.Pasture;
	import org.liveboardgames.agricola.vo.player.Player;
	import org.liveboardgames.common.dictionary.collections.HashCollection;
	import org.liveboardgames.common.dictionary.collections.HashSet;
	import org.liveboardgames.common.dictionary.collections.IMap;
	import org.liveboardgames.common.utils.collections.ModificationUtils;

	public class GameHelper
	{
		public static const MAX_STABLES:int = 4;

		private static const MAX_FENCES:int = 15;

		public static const NUMBER_OF_TURNS:int = 14;

		public static const NUMBER_OF_PEEPS:int = 5;

		public static const HARVEST_ROUNDS:HashSet = getHarvestRounds();

		public static function addNextActionCard(game:Game):void
		{
			// Get the next card in the waiting list
			var actionCards:ListCollectionView = game.waitingActionCards;
			if (actionCards != null && actionCards.length > 0)
			{
				var actionCard:ActionCard = actionCards.removeItemAt(0) as ActionCard;

				// Find the first tile (in the season tiles) that 
				// has no action card attached
				var firstEmptySeasonTile:BoardTile = findFirstEmptySeasonTile(game);

				// Update its action card
				if (firstEmptySeasonTile != null)
				{
					firstEmptySeasonTile.actionCard = actionCard;
				}
			}
		}

		/**
		 * Retrieve all the actions pending that should trigger at the
		 * beginning of a new round.
		 * The start of round actions can come either from the board
		 * or from the cards the players themselves have put in
		 * front of them.
		 */
		public static function getAllStartOfRoundActions(game:Game):ListCollectionView
		{
			var startActions:ListCollectionView = new ArrayCollection();

			// Retrieve all the cards that have been put in play
			var playedCards:ListCollectionView = getAllPlayedCards(game).getKeys();
			for each (var card:Card in playedCards)
			{
				var actions:ListCollectionView = card.actions;
				if (actions != null)
				{
					for each (var cardAction:Action in actions)
					{
						if (cardAction.tempo == ActionTempoEnum.START_OF_ROUND)
						{
							startActions.addItem(cardAction);
						}
					}
				}
			}

			return startActions;
		}

		public static function isAllowedToPlayPeep(game:Game, player:Player):Boolean
		{
			var result:Boolean = false;
			// If the current player is not the one passed as argument, or
			// if the current player has already played, it is not possible.
			if ((game.currentPlayer == player) && !game.hasPlayerPlayedPeep)
			{
				result = true;
			}
			return result;
		}

		public static function isActionCardPlayable(card:ActionCard, player:Player):Boolean
		{
			var isPlayable:Boolean = false;
			// TODO: this will need to be overridden, possibly in the ActionCheck
			// with the extensions
			if (card != null && (card.peeps == null || card.peeps.length == 0))
			{
				var actions:ListCollectionView = card.actions;
				for each (var action:Action in actions)
				{

					var overriddenAction:Action = new ActionOverrider().overrideAction(action, player);
					isPlayable = (isPlayable || new ActionCheck(overriddenAction, player, null, null).isActionAllowed());
				}
			}
			return isPlayable;
		}

		public static function canAfford(player:Player, improvement:ImprovementCard):Boolean
		{
			var cost:IMap = improvement.cost
			var alternativeCosts:ListCollectionView = improvement.alternativeCosts;
			return canPay(player, cost, alternativeCosts);
		}

		public static function canPay(player:Player, cost:IMap, alternativeCosts:ListCollectionView = null):Boolean
		{
			if (cost == null && (alternativeCosts == null || alternativeCosts.length == 0))
			{
				return true;
			}

			var result:Boolean = true;

			// iterate on each of the items in the cost
			for each (var element:Object in cost.getKeys())
			{
				var resourceCost:int = cost.getValue(element);
				if (ResourceTypeEnum.VALUES.contains(element))
				{
					result = result && checkResource(player, element as String, resourceCost);
				}
				else if (AlternativeCostEnum.DISCARD_FIREPLACE == element)
				{
					result = result && hasImprovement(player, ImprovementTypeEnum.FIREPLACE);
				}
				else
				{
					trace("Not supported cost item " + element);
				}
			}

			if (alternativeCosts != null)
			{
				// The user can afford the item if they can pay at least one of the costs
				for each (var alternativeCost:IMap in alternativeCosts)
				{
					result = result || canPay(player, alternativeCost);
				}
			}

			return result;
		}

		public static function getHouseMaterial(farm:Farm):String
		{
			var firstTile:FarmTile = farm.getTileAt(0);
			var room:String = firstTile.farmElement;
			return FarmElement.getRoomMaterial(room);
		}

		public static function canRenovateToMaterial(farm:Farm, targetMaterial:String):Boolean
		{
			var result:Boolean = false;

			// The material the farm is currently built in
			var houseMaterial:String = GameHelper.getHouseMaterial(farm);

			if (HouseTypeEnum.WOODEN == houseMaterial && HouseTypeEnum.CLAY == targetMaterial)
			{
				result = true;
			}
			else if (HouseTypeEnum.CLAY == houseMaterial && HouseTypeEnum.STONE == targetMaterial)
			{
				result = true;
			}

			return result;
		}

		public static function getOvens(farm:Farm):ListCollectionView
		{
			var ovens:ListCollectionView = new ArrayCollection();
			var playedCards:ListCollectionView = farm.playedCards;
			for each (var card:Card in playedCards)
			{
				if (card is Oven)
				{
					ovens.addItem(card);
				}
			}
			return ovens;
		}

		public static function getRemainingStables(farm:Farm):int
		{
			var builtStables:int;
			for each (var tile:FarmTile in farm.tiles)
			{
				if (tile.farmElement == FarmElement.STABLE)
				{
					builtStables++;
				}
			}
			return MAX_STABLES - builtStables;
		}

		public static function getRemainingFences(farm:Farm):int
		{
			var builtFences:ListCollectionView = getBuiltFencesIDs(farm);
			var remainingWoods:int = farm.resources.getValue(ResourceTypeEnum.WOOD);

			return Math.min(MAX_FENCES - builtFences.length, remainingWoods);
		}

		/** Get the whole list of fences for the farm. */
		public static function getBuiltFencesIDs(farm:Farm):ListCollectionView
		{
			var fenceIDs:ListCollectionView = new ArrayCollection();

			var countedTiles:ListCollectionView = new ArrayCollection();

			// Iterate on each of the tile of the farm
			for each (var tile:FarmTile in farm.tiles)
			{
				// Only the ones that belong to a pasture are of interest 
				var currentPasture:Pasture = tile.pasture;

				if (currentPasture == null)
				{
					continue;
				}

				// In this case, check each of the fences
				var id:int = tile.id;

				// 1. Left fence
				var leftFenceID:int = getFenceID(tile, DirectionEnum.LEFT);
				// If the tile is on the left (and belongs to a pasture), this means that there must 
				// be a fence on the left
				if (id < 3)
				{
					fenceIDs.addItem(leftFenceID);
				}
				// Otherwise, if the field on the left is in another pasture 
				// (and has not been dealt with yet), there is a fence
				else
				{
					var leftTile:FarmTile = farm.getTileAt(id - 3);
					var leftPasture:Pasture = leftTile.pasture;

					if (!countedTiles.contains(leftTile) && currentPasture != leftPasture)
					{
						fenceIDs.addItem(leftFenceID);
					}
				}

				// 2. Right fence
				var rightFenceID:int = getFenceID(tile, DirectionEnum.RIGHT);
				// If the tile is on the right (and belongs to a pasture), this means that there must 
				// be a fence on the right
				if (id > 11)
				{
					fenceIDs.addItem(rightFenceID);
				}
				// Otherwise, if the field on the right is in another pasture 
				// (and has not been dealt with yet), there is a fence
				else
				{
					var rightTile:FarmTile = farm.getTileAt(id + 3);
					var rightPasture:Pasture = rightTile.pasture;

					if (!countedTiles.contains(rightTile) && currentPasture != rightPasture)
					{
						fenceIDs.addItem(rightFenceID);
					}
				}

				// 3. Top fence
				var topFenceID:int = getFenceID(tile, DirectionEnum.TOP);
				// If the tile is on the top (and belongs to a pasture), this means that there must 
				// be a fence on the top
				if ((id % 3) == 2)
				{
					fenceIDs.addItem(topFenceID);
				}
				// Otherwise, if the field on the top is in another pasture 
				// (and has not been dealt with yet), there is a fence
				else
				{
					var topTile:FarmTile = farm.getTileAt(id + 1);
					var topPasture:Pasture = topTile.pasture;

					if (!countedTiles.contains(topTile) && currentPasture != topPasture)
					{
						fenceIDs.addItem(topFenceID);
					}
				}

				// 4. Bottom fence
				var bottomFenceID:int = getFenceID(tile, DirectionEnum.BOTTOM);
				// If the tile is at the bottom (and belongs to a pasture), this means that there must 
				// be a fence at the bottom
				if ((id % 3) == 0)
				{
					fenceIDs.addItem(bottomFenceID);
				}
				// Otherwise, if the field at the bottom is in another pasture 
				// (and has not been dealt with yet), there is a fence
				else
				{
					var bottomTile:FarmTile = farm.getTileAt(id - 1);
					var bottomPasture:Pasture = bottomTile.pasture;

					if (!countedTiles.contains(bottomTile) && currentPasture != bottomPasture)
					{
						fenceIDs.addItem(bottomFenceID);
					}
				}

				countedTiles.addItem(tile);
			}

			return fenceIDs;
		}

		public static function hasEmptyRoom(game:Game, player:Player):Boolean
		{
			var result:Boolean = false;

			var numberOfRooms:int = GameHelper.getNumberOfRooms(player.farm);
			var numberOfPeeps:int = GameHelper.countPeepsForPlayer(game, player);

			result = (numberOfRooms > numberOfPeeps);

			return result;
		}

		public static function hasEmptySpace(farm:Farm):Boolean
		{
			var result:Boolean = false;
			var tiles:ListCollectionView = farm.tiles;
			for each (var tile:FarmTile in tiles)
			{
				var element:String = tile.farmElement;
				var pasture:Pasture = tile.pasture;
				if (element == null && pasture == null)
				{
					result = true;
					break;
				}
			}
			return result;
		}

		public static function hasPeepLeftForPlayer(game:Game, player:Player):Boolean
		{
			// Get the total number of peeps
			var totalPeeps:int = GameHelper.countPeepsForPlayer(game, player);

			var result:Boolean = false;
			if (totalPeeps < NUMBER_OF_PEEPS)
			{
				result = true;
			}

			return result;
		}

		public static function countPeepsForPlayer(game:Game, player:Player):int
		{
			// Count number of peeps played on the board
			var peepsOnBoard:int = GameHelper.countPeepsOnBoardForPlayer(game, player);

			// Count number of peeps still in the farm
			var peepsOnFarm:int = GameHelper.countPeepsOnFarm(player.farm);

			var totalPeeps:int = peepsOnBoard + peepsOnFarm;

			return totalPeeps;
		}

		public static function countPeepsOnBoardForPlayer(game:Game, player:Player):int
		{
			var numberOfPeeps:int = 0;

			var tiles:ListCollectionView = game.tiles;
			for each (var tile:BoardTile in tiles)
			{
				var actionCard:Card = tile.actionCard;
				if (actionCard != null)
				{
					var peeps:ListCollectionView = actionCard.peeps;
					if (peeps != null)
					{
						for each (var peep:Peep in peeps)
						{
							var owner:Player = getPeepOwner(game, peep);
							if (owner == player)
							{
								numberOfPeeps++;
							}
						}
					}
				}
			}

			return numberOfPeeps;
		}

		public static function getPeepOwner(game:Game, peep:Peep):Player
		{
			var owner:Player = null;

			for each (var player:Player in game.players)
			{
				for each (var playerPeep:Peep in player.peeps)
				{
					if (playerPeep.id == peep.id)
					{
						owner = player;
					}
				}
			}

			return owner;
		}

		public static function countPeepsOnFarm(farm:Farm):int
		{
			var peepsOnFarm:ListCollectionView = getPeepsOnFarm(farm);
			var numberOfPeeps:int = peepsOnFarm.length;

			return numberOfPeeps;
		}

		public static function getPeepsOnFarm(farm:Farm):ListCollectionView
		{
			var peepsOnFarm:ListCollectionView = new ArrayCollection();

			var tiles:ListCollectionView = farm.tiles;
			for each (var tile:FarmTile in tiles)
			{
				var peeps:ListCollectionView = tile.peeps;
				if (peeps != null)
				{
					for each (var peep:Peep in peeps)
					{
						peepsOnFarm.addItem(peep);
					}
				}
			}

			return peepsOnFarm;
		}

		public static function getNumberOfRooms(farm:Farm):int
		{
			var numberOfElements:int;

			for each (var tile:FarmTile in farm.tiles)
			{
				if (FarmElement.isRoom(tile.farmElement))
				{
					numberOfElements++;
				}
			}

			return numberOfElements;
		}

		public static function getNumberOfFields(farm:Farm):int
		{
			var numberOfElements:int;

			for each (var tile:FarmTile in farm.tiles)
			{
				if (tile.farmElement == FarmElement.FIELD)
				{
					numberOfElements++;
				}
			}

			return numberOfElements;
		}

		public static function getNumberOfPastures(farm:Farm):int
		{
			var numberOfPastures:int;

			var countedPastures:ListCollectionView = new ArrayCollection();

			for each (var tile:FarmTile in farm.tiles)
			{
				var pasture:Pasture = tile.pasture;

				if (pasture != null)
				{
					var id:int = pasture.id;
					if (!countedPastures.contains(id))
					{
						countedPastures.addItem(id);
						numberOfPastures++;
					}
				}
			}

			return numberOfPastures;
		}

		public static function getFarmTileContainingPeep(game:Game, peep:Peep):FarmTile
		{
			var farmTile:FarmTile = null;

			if (peep != null)
			{
				var farm:Farm = getPeepOwner(game, peep).farm;
				for each (var tile:FarmTile in farm.tiles)
				{
					if (tile.peeps != null && tile.peeps.contains(peep))
					{
						farmTile = tile;
						break;
					}
				}
			}

			return farmTile;
		}

		public static function getAutomaticActions(actions:ListCollectionView):ListCollectionView
		{
			var result:ListCollectionView = new ArrayCollection();
			for each (var action:Action in actions)
			{
				if (!action.needPlayerInput)
				{
					result.addItem(action);
				}
			}
			return result;
		}

		public static function getPlayableActions(actions:ListCollectionView, player:Player, playedActions:ListCollectionView,
			exclusiveActions:ListCollectionView):ListCollectionView
		{
			var playableActions:ListCollectionView = new ArrayCollection();
			for each (var action:Action in actions)
			{
				// Check whether the user can use the action					
				var canExecuteAction:Boolean = new ActionCheck(action, player, playedActions, exclusiveActions).isActionAllowed();
				if (canExecuteAction)
				{
					playableActions.addItem(action);
				}
			}
			return playableActions;
		}

		public static function findNextPlayerToPlay(game:Game):Player
		{
			var nextPlayer:Player = null;

			// Is there still a player who has peeps to play?
			var haveAllPlayersPlayed:Boolean = haveAllPlayersPlayed(game);
			if (!haveAllPlayersPlayed)
			{
				// The player who is currently playing
				var player:Player = game.currentPlayer;

				// If no one is currently playing, the first player 
				// in the list will be the next one to have their turn.
				var players:ListCollectionView = game.players;
				if (player == null)
				{
					nextPlayer = players.getItemAt(0) as Player;
				}
				// Else, we get the next player in the list who has a 
				// peep to play
				else
				{
					// The position of the current player in the list
					var currentPosition:int = players.getItemIndex(player);

					// iterate over all the players of the game until we find 
					// one who could play
					var numberOfIterations:int = 0;
					while (numberOfIterations++ < players.length)
					{
						// The next player can be the first one, if the current player 
						// is the last one of the list
						var nextPlayerIndex:int = (++currentPosition % players.length);

						// Does this player still have a peep to play?
						var potentialNextPlayer:Player = players.getItemAt(nextPlayerIndex) as Player;
						var peeps:ListCollectionView = GameHelper.getPeepsOnFarm(potentialNextPlayer.farm);
						if (peeps != null && peeps.length > 0)
						{
							nextPlayer = potentialNextPlayer;
							break;
						}
					}
				}
			}
			return nextPlayer;
		}

		public static function getPeepTriggeredActions(card:Card):ListCollectionView
		{
			// Process the actions
			var actions:ListCollectionView = card.actions;
			var peepActions:ListCollectionView = new ArrayCollection();

			for each (var action:Action in actions)
			{
				// Check that the action is one that should be triggered
				// by the placement of a peep
				var tempo:String = action.tempo;
				if (ActionTempoEnum.PEEP_TRIGGERED == tempo)
				{
					peepActions.addItem(action);
				}
			}

			return peepActions;
		}

		public static function sendAllPeepsHome(game:Game):void
		{
			// Iterate over the game to find all played cards
			var playedCards:ListCollectionView = GameHelper.getAllPlayedCards(game).getKeys();

			// Iterate over the played cards to find potential peeps
			for each (var card:Card in playedCards)
			{
				var peeps:ListCollectionView = card.peeps;
				// If there are peeps on the tile
				if (peeps != null)
				{
					for each (var peep:Peep in peeps)
					{
						// Add a peep on the owner's farm
						var owner:Player = getPeepOwner(game, peep);
						GameHelper.addPeep(owner.farm, peep);
					}
					// Then remove all peeps from the tile
					card.peeps = null;
				}
			}
		}

		public static function addPeep(farm:Farm, peep:Peep):void
		{
			var tiles:ListCollectionView = farm.tiles;
			// Try to fit one peep per tile
			var allRoomsFull:Boolean = true;
			for each (var tile:FarmTile in tiles)
			{
				if (FarmElement.isRoom(tile.farmElement))
				{
					if (tile.peeps == null || tile.peeps.length == 0)
					{
						tile.addPeep(peep);
						allRoomsFull = false;
						break;
					}
				}
			}

			// All rooms full, needs to overload a room
			if (allRoomsFull)
			{
				var firstTile:FarmTile = tiles.getItemAt(0) as FarmTile;
				// Add another peep to the first room
				firstTile.addPeep(peep);
			}
		}

		public static function canPloughField(farm:Farm, farmTile:FarmTile):Boolean
		{
			var result:Boolean = false;

			var construction:String = farmTile.farmElement;
			if (construction == null)
			{
				// If there is another field already ploughed, check that it is in one
				// of the adjacent tiles
				if (GameHelper.isFieldPloughed(farm))
				{
					for each (var tile:FarmTile in farm.tiles)
					{
						var currentConstruction:String = tile.farmElement
						// We suppose that the farm is properly built already. As such, if there
						// is at least one field adjacent to the current tile, it is ok
						if (currentConstruction == FarmElement.FIELD && GameHelper.areAdjacent(tile, farmTile))
						{
							result = true;
							break;
						}
					}
				}
				else
				{
					result = true;
				}
			}

			return result;
		}

		public static function isFieldPloughed(farm:Farm):Boolean
		{
			var result:Boolean = false;
			for each (var farmTile:FarmTile in farm.tiles)
			{
				var farmElement:String = farmTile.farmElement;
				if (farmElement == FarmElement.FIELD)
				{
					result = true;
					break;
				}
			}
			return result;
		}

		public static function areAdjacent(tile1:FarmTile, tile2:FarmTile):Boolean
		{
			var result:Boolean = false;
			var id1:int = tile1.id;
			var id2:int = tile2.id;

			// If the two tiles are adjacent on the same line, their IDs are 
			// separated by 3
			if (Math.abs(id1 - id2) == 3)
			{
				result = true;
			}
			// If they are separated by 1...
			else if (Math.abs(id1 - id2) == 1)
			{
				//... check that one of them is in the middle of a column
				if (((id1 - 1) % 3 == 0) || ((id2 - 1) % 3 == 0))
				{
					result = true;
				}
			}
			return result;
		}

		public static function getNbAffordableItems(farm:Farm, cost:IMap):int
		{
			var nbOfAffordableItems:int = -1;

			var costKeys:ListCollectionView = cost.getKeys();
			var resources:HashCollection = farm.resources;

			for each (var costKey:String in costKeys)
			{
				// Cost of the item in this resource
				var itemCost:int = cost.getValue(costKey);
				// Number of items the player could afford if it costed only this
				var resource:int = resources.getValue(costKey);
				var nbOfAfford:int = Math.floor(resource / itemCost);
				// The number of affordable items is simply the min of all of them
				if (nbOfAffordableItems == -1)
				{
					nbOfAffordableItems = nbOfAfford;
				}
				else
				{
					nbOfAffordableItems = Math.min(nbOfAffordableItems, nbOfAfford);
				}
			}

			return nbOfAffordableItems;
		}

		public static function canBuildRoom(farm:Farm, farmTile:FarmTile):Boolean
		{
			var result:Boolean = false;

			var construction:String = farmTile.farmElement;
			if (construction == null)
			{
				for each (var tile:FarmTile in farm.tiles)
				{
					var currentConstruction:String = tile.farmElement;
					// We suppose that the farm is properly built already.
					if (FarmElement.isRoom(currentConstruction) && areAdjacent(tile, farmTile))
					{
						result = true;
						break;
					}
				}
			}
			return result;
		}

		public static function pay(farm:Farm, cost:IMap, game:Game = null):void
		{
			if (cost != null)
			{
				var keys:ListCollectionView = cost.getKeys();
				for each (var element:Object in keys)
				{
					var resourceCost:int = cost.getValue(element);
					if (element is String && ResourceTypeEnum.VALUES.contains(element))
					{
						payResources(farm, element as String, cost.getValue(element) as int);
					}
					else if (AlternativeCostEnum.DISCARD_FIREPLACE == element)
					{
						discardFireplace(farm, game);
					}
					else
					{
						throw new Error("Cost not supported for element " + element);
					}
				}
			}
		}

		public static function getPossiblePayments(player:Player, improvement:ImprovementCard):ListCollectionView
		{
			var possiblePayments:ListCollectionView;

			if (improvement != null)
			{
				possiblePayments = new ArrayCollection();
				if (GameHelper.canPay(player, improvement.cost))
				{
					possiblePayments.addItem(improvement.cost);
				}
				if (improvement.alternativeCosts != null)
				{
					for each (var alternativeCost:IMap in improvement.alternativeCosts)
					{
						if (GameHelper.canPay(player, alternativeCost))
						{
							possiblePayments.addItem(alternativeCost);
						}
					}
				}
			}

			return possiblePayments;
		}

		public static function hasMultiplePossiblePayments(player:Player, improvement:ImprovementCard):Boolean
		{
			var result:Boolean = false;

			if (improvement != null)
			{
				var possiblePayments:ListCollectionView = getPossiblePayments(player, improvement);

				if (possiblePayments.length > 1)
				{
					result = true;
				}
			}

			return result;
		}

		public static function getTileForRound(game:Game, roundNumber:int):BoardTile
		{
			var boardTile:BoardTile = game.tiles.getItemAt(roundNumber - 1 + 16) as BoardTile;
			return boardTile;
		}

		public static function getCurrentRoundNumber(game:Game):int
		{
			var turnsToPlay:int = game.waitingActionCards.length;
			var currentRoundNumber:int = NUMBER_OF_TURNS - turnsToPlay;
			return currentRoundNumber;
		}

		public static function getPlayTriggeredActions(card:Card):ListCollectionView
		{
			var result:ListCollectionView = new ArrayCollection();
			var actions:ListCollectionView = card.actions;
			if (actions != null)
			{
				for each (var action:Action in actions)
				{
					if (ActionTempoEnum.PLAY_TRIGGERED == action.tempo)
					{
						result.addItem(action);
					}
				}
			}
			return result;
		}

		public static function canSowField(farmTile:FarmTile):Boolean
		{
			var result:Boolean = false;
			if (farmTile != null)
			{
				var existingConstruction:String = farmTile.farmElement;
				// If there is a field with no resource on it
				if (existingConstruction == FarmElement.FIELD && (farmTile.resources == null || farmTile.resources.getKeys().
					length == 0))
				{
					result = true;
				}
			}
			return result;
		}

		/**
		 * This method should be called at the beginning of the round only,
		 * so all peeps should be on their farms.
		 */
		public static function growUp(game:Game):void
		{
			var players:ListCollectionView = game.players;
			for each (var player:Player in players)
			{
				var farm:Farm = player.farm;
				GameHelper.growUpOnFarm(farm);
			}
		}

		public static function growUpOnFarm(farm:Farm):void
		{
			var peeps:ListCollectionView = GameHelper.getPeepsOnFarm(farm);
			for each (var peep:Peep in peeps)
			{
				peep.status = BirthStatusEnum.ADULT;
			}
		}

		public static function getCookingPlaces(farm:Farm):ListCollectionView
		{
			var result:ListCollectionView = new ArrayCollection();
			var playedCards:ListCollectionView = farm.playedCards;
			for each (var card:Card in playedCards)
			{
				if (card is CookingPlace)
				{
					result.addItem(card);
				}
			}
			return result;
		}

		public static function computeAvailableFences(farm:Farm):int
		{
			var remainingFences:int = GameHelper.getRemainingFences(farm);
			var remainingWoods:int = farm.resources.getValue(ResourceTypeEnum.WOOD);

			return Math.min(remainingFences, remainingWoods);
		}

		public static function canHostAnimal(farmTile:FarmTile):Boolean
		{
			var farmElement:String = farmTile.farmElement;
			var pasture:Pasture = farmTile.pasture;
			var result:Boolean = false;
			if (FarmElement.isRoom(farmElement) || farmElement == FarmElement.STABLE || pasture != null)
			{
				result = true;
			}

			return result;
		}

		public static function isAnimalDispositionValid(farm:Farm):Boolean
		{
			var result:Boolean = true;

			// Check the animals inside the house
			result = result && validateAnimalsInHouse(farm);

			// Check each pasture
			var pastures:ListCollectionView = getPastures(farm);
			for each (var pasture:Pasture in pastures)
			{
				var pastureTiles:ListCollectionView = getPastureTiles(farm, pasture);
				result = result && validateAnimalsOnPasture(farm, pastureTiles);
			}

			return result;
		}

		public static function canBuildPastureOnTile(farm:Farm, farmTile:FarmTile):Boolean
		{
			var result:Boolean = false;

			var construction:String = farmTile.farmElement;

			if (construction == null || construction == FarmElement.STABLE)
			{
				result = true;
			}

			return result;
		}

		public static function canAddPastureTile(farm:Farm, farmTile:FarmTile, currentPastureTiles:ListCollectionView, remainingFences:int):Boolean
		{
			var canAddPastureTile:Boolean = false;

			// What the new pasture would look like
			var newPastureTiles:ListCollectionView = new ArrayCollection();
			for each (var tile:FarmTile in currentPastureTiles)
			{
				newPastureTiles.addItem(tile);
			}
			newPastureTiles.addItem(farmTile);

			// First check that all selected tiles form one pasture, i.e. that are adjacent 
			// to one another
			canAddPastureTile = GameHelper.isPastureValid(farm, newPastureTiles);

			// Then check that if a pasture has already been built, at least one tile of the new 
			// pasture is adjacent to it
			canAddPastureTile = canAddPastureTile && GameHelper.isPasturePositionValid(farm, newPastureTiles);

			if (canAddPastureTile)
			{
				// Compute the number of fences needed to build a pasture with the current tiles
				var pastureFenceSize:int = GameHelper.computePastureFenceSize(farm, newPastureTiles);

				canAddPastureTile = (pastureFenceSize <= remainingFences);
			}

			return canAddPastureTile;
		}

		public static function canRemovePastureTile(farm:Farm, farmTile:FarmTile, currentPastureTiles:ListCollectionView,
			remainingFences:int):Boolean
		{
			// What the new pasture would look like
			var newPastureTiles:ListCollectionView = new ArrayCollection();
			for each (var tile:FarmTile in currentPastureTiles)
			{
				newPastureTiles.addItem(tile);
			}
			newPastureTiles.removeItemAt(newPastureTiles.getItemIndex(farmTile));

			// Check if the new pasture would be valid
			var canRemovePasture:Boolean = GameHelper.isPastureValid(farm, newPastureTiles);

			// Then check that if a pasture has already been built, at least one tile of the new 
			// pasture is adjacent to it
			canRemovePasture = canRemovePasture && GameHelper.isPasturePositionValid(farm, newPastureTiles);

			if (canRemovePasture)
			{
				// Compute the number of fences that would be needed to reform the current pasture with the 
				// tile removed
				var pastureFenceSize:int = GameHelper.computePastureFenceSize(farm, newPastureTiles);

				canRemovePasture = (pastureFenceSize <= remainingFences);
			}

			return canRemovePasture;
		}

		public static function isHarvest(game:Game):Boolean
		{
			var isHarvest:Boolean = false;

			var waitingActions:ListCollectionView = game.waitingActionCards;
			var currentRound:int = NUMBER_OF_TURNS - waitingActions.length;
			if (HARVEST_ROUNDS.has(currentRound))
			{
				isHarvest = true;
			}

			return isHarvest;
		}

		public static function findNextPlayerToHarvest(game:Game):Player
		{
			var nextPlayer:Player = null;

			// The first player to harvest is the first player of the turn
			var players:ListCollectionView = game.players;

			for each (var player:Player in players)
			{
				if (!player.hasHarvested)
				{
					nextPlayer = player;
					break;
				}
			}

			return nextPlayer;
		}

		public static function computeFoodForHarvest(player:Player):int
		{
			var food:int = 0;

			// Need two foods per peep, and one per newborn
			var peeps:ListCollectionView = GameHelper.getPeepsOnFarm(player.farm);
			for each (var peep:Peep in peeps)
			{
				if (peep.status == BirthStatusEnum.NEWBORN)
				{
					food = food + 1;
				}
				else
				{
					food = food + 2;
				}
			}

			return food;
		}

		public static function getNewBornAnimals(farm:Farm):HashCollection
		{
			var newBornAnimals:HashCollection = null;

			var animalsOnFarm:IMap = GameHelper.getAnimalsOnFarm(farm);
			for each (var animalType:String in animalsOnFarm.getKeys())
			{
				var quantity:int = animalsOnFarm.getValue(animalType);
				if (quantity >= 2)
				{
					if (newBornAnimals == null)
					{
						newBornAnimals = new HashCollection();
					}
					newBornAnimals.put(animalType, 1);
				}
			}

			return newBornAnimals;
		}

		public static function getAnimalsOnFarm(farm:Farm):IMap
		{
			var animalsOnFarm:IMap = new HashCollection();

			for each (var farmTile:FarmTile in farm.tiles)
			{
				var animalsOnTile:IMap = getAnimalsOnTile(farmTile);
				animalsOnFarm = ModificationUtils.add(animalsOnFarm, animalsOnTile);
			}

			return animalsOnFarm;
		}

		public static function resetHarvest(game:Game):void
		{
			game.harvestFieldPlayers = new ArrayCollection();
			game.harvestFeedPlayers = new ArrayCollection();
			game.harvestBreedPlayers = new ArrayCollection();
		}

		public static function getCardsUsableAtTime(cards:ListCollectionView, tempo:String):ListCollectionView
		{
			var result:ListCollectionView = new ArrayCollection();

			for each (var card:Card in cards)
			{
				var actions:ListCollectionView = card.actions;
				if (actions != null)
				{
					for each (var cardAction:Action in actions)
					{
						if (cardAction.tempo == tempo)
						{
							result.addItem(card);
						}
					}
				}
			}

			return result;
		}

		public static function getAllPeeps(game:Game):ListCollectionView
		{
			var peeps:ListCollectionView = new ArrayCollection();

			for each (var player:Player in game.players)
			{
				for each (var peep:Peep in player.peeps)
				{
					peeps.addItem(peep);
				}
			}

			return peeps;
		}

		private static function isPastureValid(farm:Farm, pastureTiles:ListCollectionView):Boolean
		{
			var result:Boolean = isPastureCompact(pastureTiles);

			// Check that the soon-to-be pasture does not cover two existing pastures 
			// (it should either be a totally new one, or a subdivision of a single existing pasture)
			if (pastureTiles != null && pastureTiles.length > 0)
			{
				var firstPasture:FarmTile = pastureTiles.getItemAt(0) as FarmTile;
				var currentPasture:Pasture = firstPasture.pasture as Pasture;
				for each (var pastureTile:FarmTile in pastureTiles)
				{
					if (currentPasture != pastureTile.pasture)
					{
						result = false;
					}
				}
			}

			return result;
		}

		private static function isPastureCompact(pastureTiles:ListCollectionView):Boolean
		{
			var result:Boolean = true;

			// Check that the pasture is "compact"
			if (pastureTiles.length >= 2)
			{
				var compactPastureTiles:ListCollectionView = new ArrayCollection();
				compactPastureTiles.addItem(pastureTiles.getItemAt(0));

				// TODO: unefficient loop for now
				for (var i:int = 0; i < pastureTiles.length; i++)
				{
					// For each tile, check that it is adjacent to at least one tile of the compact representation
					for each (var tile:FarmTile in pastureTiles)
					{
						for each (var otherTile:FarmTile in compactPastureTiles)
						{
							if (tile != otherTile && areAdjacent(tile, otherTile) && !compactPastureTiles.contains(tile))
							{
								compactPastureTiles.addItem(tile);
							}
						}
					}
				}
				result = result && (compactPastureTiles.length == pastureTiles.length);
			}

			return result;
		}

		private static function isPasturePositionValid(farm:Farm, pastureTiles:ListCollectionView):Boolean
		{
			// Build the image of the new full pasture (all pasture tiles of the farm)
			var newPastureTiles:ListCollectionView = new ArrayCollection();
			for each (var pastureTile:FarmTile in pastureTiles)
			{
				newPastureTiles.addItem(pastureTile);
			}
			for each (var farmTile:FarmTile in farm.tiles)
			{
				if (farmTile.pasture != null && !newPastureTiles.contains(farmTile))
				{
					newPastureTiles.addItem(farmTile);
				}
			}

			var result:Boolean = isPastureCompact(newPastureTiles);

			return result;
		}

		private static function computePastureFenceSize(farm:Farm, pastureTiles:ListCollectionView):int
		{
			var newFences:ListCollectionView = getNewFences(farm, pastureTiles);
			return newFences.length;
		}

		public static function getNewFences(farm:Farm, pastureTiles:ListCollectionView):ListCollectionView
		{
			var totalFences:ListCollectionView = getBuiltFencesIDs(farm);
			var pastureFences:ListCollectionView = getPastureFenceIDs(farm, pastureTiles);

			var newFences:ListCollectionView = new ArrayCollection();

			for each (var fenceID:int in pastureFences)
			{
				if (!totalFences.contains(fenceID))
				{
					newFences.addItem(fenceID);
				}
			}

			return newFences;
		}

		public static function getAllFarmTiles(game:Game):ListCollectionView
		{
			var farmTiles:ListCollectionView = new ArrayCollection();

			for each (var player:Player in game.players)
			{
				for each (var tile:FarmTile in player.farm.tiles)
				{
					farmTiles.addItem(tile);
				}
			}

			return farmTiles;
		}

		/** The total number of fences needed by a set of tiles (usually a single pasture). */
		private static function getPastureFenceIDs(farm:Farm, pastureTiles:ListCollectionView):ListCollectionView
		{
			var fenceIDs:ListCollectionView = new ArrayCollection();

			var countedTiles:ListCollectionView = new ArrayCollection();

			var currentPasture:Pasture = FarmFactory.getNewPasture(farm);

			// Iterate on each of the tile of the farm
			for each (var tile:FarmTile in pastureTiles)
			{
				// In this case, check each of the fences
				var id:int = tile.id;

				// 1. Left fence
				var leftFenceID:int = getFenceID(tile, DirectionEnum.LEFT);
				// If the tile is on the left (and belongs to a pasture), this means that there must 
				// be a fence on the left
				if (id < 3)
				{
					fenceIDs.addItem(leftFenceID);
				}
				// Otherwise, if the field on the left is in another pasture 
				// (and has not been dealt with yet), there is a fence
				else
				{
					var leftTile:FarmTile = farm.getTileAt(id - 3);
					var leftPasture:Pasture = leftTile.pasture;

					if (!countedTiles.contains(leftTile) && currentPasture != leftPasture)
					{
						fenceIDs.addItem(leftFenceID);
					}

					if (countedTiles.contains(leftTile))
					{
						fenceIDs.removeItemAt(fenceIDs.getItemIndex(leftFenceID));
					}
				}

				// 2. Right fence
				var rightFenceID:int = getFenceID(tile, DirectionEnum.RIGHT);
				// If the tile is on the right (and belongs to a pasture), this means that there must 
				// be a fence on the right
				if (id > 11)
				{
					fenceIDs.addItem(rightFenceID);
				}
				// Otherwise, if the field on the right is in another pasture 
				// (and has not been dealt with yet), there is a fence
				else
				{
					var rightTile:FarmTile = farm.getTileAt(id + 3);
					var rightPasture:Pasture = rightTile.pasture;

					if (!countedTiles.contains(rightTile) && currentPasture != rightPasture)
					{
						fenceIDs.addItem(rightFenceID);
					}

					if (countedTiles.contains(rightTile))
					{
						fenceIDs.removeItemAt(fenceIDs.getItemIndex(rightFenceID));
					}
				}

				// 3. Top fence
				var topFenceID:int = getFenceID(tile, DirectionEnum.TOP);
				trace("Examining top fence " + topFenceID);
				// If the tile is on the top (and belongs to a pasture), this means that there must 
				// be a fence on the top
				if ((id % 3) == 2)
				{
					trace("At the top, adding fence");
					fenceIDs.addItem(topFenceID);
				}
				// Otherwise, if the field on the top is in another pasture 
				// (and has not been dealt with yet), there is a fence
				else
				{
					var topTile:FarmTile = farm.getTileAt(id + 1);
					var topPasture:Pasture = topTile.pasture;

					if (!countedTiles.contains(topTile) && currentPasture != topPasture)
					{
						fenceIDs.addItem(topFenceID);
						trace("adding top fence");
					}

					if (countedTiles.contains(topTile))
					{
						fenceIDs.removeItemAt(fenceIDs.getItemIndex(topFenceID));
						trace("removing top fence");
					}
				}

				// 4. Bottom fence
				var bottomFenceID:int = getFenceID(tile, DirectionEnum.BOTTOM);
				trace("Examining bottom fence " + bottomFenceID);
				// If the tile is at the bottom (and belongs to a pasture), this means that there must 
				// be a fence at the bottom
				if ((id % 3) == 0)
				{
					fenceIDs.addItem(bottomFenceID)
					trace("At the bottom, adding fence");
					;
				}
				// Otherwise, if the field at the bottom is in another pasture 
				// (and has not been dealt with yet), there is a fence
				else
				{
					var bottomTile:FarmTile = farm.getTileAt(id - 1);
					var bottomPasture:Pasture = bottomTile.pasture;

					if (!countedTiles.contains(bottomTile) && currentPasture != bottomPasture)
					{
						fenceIDs.addItem(bottomFenceID);
						trace("adding bottom fence");
					}

					if (countedTiles.contains(bottomTile))
					{
						fenceIDs.removeItemAt(fenceIDs.getItemIndex(bottomFenceID));
						trace("removing bottom fence");
					}
				}

				countedTiles.addItem(tile);
			}

			return fenceIDs;
		}

		private static function validateAnimalsInHouse(farm:Farm):Boolean
		{
			var farmTiles:ListCollectionView = farm.tiles;
			// First validate the animals in the house: only one animal allowed (whatever the type)
			var animalsInHouse:IMap = new HashCollection();
			for each (var farmTile:FarmTile in farmTiles)
			{
				var element:String = farmTile.farmElement;
				if (FarmElement.isRoom(element))
				{
					var animals:IMap = getAnimalsOnTile(farmTile);
					animalsInHouse = ModificationUtils.add(animals, animalsInHouse);
				}
			}

			// Check the animals
			var result:Boolean = false;
			var keys:ListCollectionView = animalsInHouse.getKeys();
			if (animalsInHouse.getKeys() == null || animalsInHouse.getKeys().length == 0)
			{
				result = true;
			}
			else if (animalsInHouse.getKeys().length == 1)
			{
				var animalType:String = animalsInHouse.getKeys()[0];
				var animalQuantity:int = animalsInHouse.getValue(animalType);
				if (animalQuantity <= 1)
				{
					result = true;
				}
			}

			return result;
		}

		private static function validateAnimalsOnPasture(farm:Farm, pastureTiles:ListCollectionView):Boolean
		{
			var result:Boolean = true;

			var animalTypesOnPasture:ListCollectionView = new ArrayCollection();
			for each (var tile:FarmTile in pastureTiles)
			{
				// Validate that there is only one type of animal on this tile
				var animals:IMap = getAnimalsOnTile(tile);
				if (animals != null && animals.getKeys() != null && animals.getKeys().length > 0)
				{
					if (animals.getKeys().length > 1)
					{
						result = false
					}
					// Validate that the number of animals in each tile is less than the maximum authorized
					else
					{
						var animalType:String = animals.getKeys()[0];
						var numberOfAnimalsOnTile:int = animals.getValue(animalType);
						var numberOfAnimalsAllowed:int = getNumberOfAnimalsAllowedOnTile(farm, pastureTiles, tile);
						if (numberOfAnimalsOnTile > numberOfAnimalsAllowed)
						{
							result = false;
						}
						else if (!animalTypesOnPasture.contains(animalType))
						{
							animalTypesOnPasture.addItem(animalType);
						}
					}
				}
			}

			// Validate that there is only one type of animal in the pasture
			if (animalTypesOnPasture.length > 1)
			{
				result = false;
			}

			return result;
		}

		private static function getAnimalsOnTile(farmTile:FarmTile):IMap
		{
			var animalsOnTile:IMap = null;
			var resourcesOnTile:IMap = farmTile.resources;
			if (resourcesOnTile != null && resourcesOnTile.getKeys().length > 0)
			{
				animalsOnTile = new HashCollection();
				for each (var resourceType:String in resourcesOnTile.getKeys())
				{
					if (ResourceTypeEnum.ANIMALS.contains(resourceType))
					{
						var quantity:int = resourcesOnTile.getValue(resourceType);
						animalsOnTile.put(resourceType, quantity);
					}
				}
			}

			return animalsOnTile;
		}

		private static function getNumberOfAnimalsAllowedOnTile(farm:Farm, pastureTiles:ListCollectionView, tile:FarmTile):int
		{
			var numberOfAnimalAllowed:int;

			// Get the number of stables on the pasture
			var numberOfStablesOfPasture:int = getNumberOfStablesOnPasture(pastureTiles);

			const baseNumberOfAnimalsAllowed:int = 2;
			numberOfAnimalAllowed = Math.pow(baseNumberOfAnimalsAllowed, numberOfStablesOfPasture + 1);

			return numberOfAnimalAllowed;
		}

		private static function getNumberOfStablesOnPasture(pastureTiles:ListCollectionView):int
		{
			var numberOfStablesOfPasture:int;

			for each (var tile:FarmTile in pastureTiles)
			{
				if (tile.farmElement == FarmElement.STABLE)
				{
					numberOfStablesOfPasture++;
				}
			}

			return numberOfStablesOfPasture;
		}

		private static function getPastures(farm:Farm):ListCollectionView
		{
			var pastures:ListCollectionView = new ArrayCollection();

			for each (var tile:FarmTile in farm.tiles)
			{
				var pasture:Pasture = tile.pasture;
				if (pasture != null && !pastures.contains(pasture))
				{
					pastures.addItem(pasture);
				}
			}

			return pastures;
		}

		private static function getPastureTiles(farm:Farm, pasture:Pasture):ListCollectionView
		{
			var pastureTiles:ListCollectionView = new ArrayCollection();

			for each (var tile:FarmTile in farm.tiles)
			{
				if (tile.pasture == pasture)
				{
					pastureTiles.addItem(tile);
				}
			}

			return pastureTiles;
		}

		private static function discardFireplace(farm:Farm, game:Game):void
		{
			if (game == null)
			{
				throw new Error("This function cannot be called with a null game");
			}

			var playedCards:ListCollectionView = farm.playedCards;
			var fireplace:ImprovementCard;

			if (playedCards != null)
			{
				for each (var card:Card in playedCards)
				{
					if (card is ImprovementCard)
					{
						var improvement:ImprovementCard = card as ImprovementCard;
						if (GameHelper.hasType(improvement, ImprovementTypeEnum.FIREPLACE))
						{
							fireplace = improvement;
						}
					}
				}
			}

			if (fireplace != null)
			{
				// Remove it from the player's cards
				var index:int = playedCards.getItemIndex(fireplace);
				playedCards.removeItemAt(index);

				// Add it back to the major improvements 
				game.addMajorImprovement(fireplace);
			}
		}

		private static function hasType(improvement:ImprovementCard, type:String):Boolean
		{
			var result:Boolean = false;
			var types:ListCollectionView = improvement.types;
			if (types != null)
			{
				for each (var cardType:String in types)
				{
					if (cardType == type)
					{
						result = true;
						break;
					}
				}
			}
			return result;
		}

		/**
		 * Fences are ordered: first horizontal, then vertical.
		 * Tiles with ID from 0 to 19 are horizontal tiles (from bottom to top, then left to right)
		 * Tiles with ID form 20 to 37 are vertical tiles (from bottom to top, then left to right)
		 */
		private static function getFenceID(tile:FarmTile, direction:DirectionEnum):int
		{
			var fenceID:int;
			var n:int = tile.id;

			switch (direction)
			{
				case DirectionEnum.BOTTOM:
					fenceID = 4 * Math.floor(n / 3) + (n % 3);
					break;
				case DirectionEnum.TOP:
					fenceID = 4 * Math.floor(n / 3) + (n % 3) + 1;
					break;
				case DirectionEnum.LEFT:
					fenceID = 20 + n;
					break;
				case DirectionEnum.RIGHT:
					fenceID = 20 + 3 + n;
					break;
			}

			return fenceID;
		}

		private static function findFirstEmptySeasonTile(game:Game):BoardTile
		{
			var firstEmptySeasonTile:BoardTile;
			for each (var tile:BoardTile in game.tiles)
			{
				if (tile.type == BoardTileTypeEnum.SEASON && tile.actionCard == null)
				{
					firstEmptySeasonTile = tile;
					break;
				}
			}
			return firstEmptySeasonTile;
		}

		/** Keys are all the cards, linked with their container. */
		public static function getAllPlayedCards(game:Game):IMap
		{
			// Iterate over the game to find all played cards
			var playedCards:IMap = new HashCollection();

			// Get the cards that have been played on the board
			var boardTiles:ListCollectionView = game.tiles;
			for each (var boardTile:BoardTile in boardTiles)
			{
				var card:Card = boardTile.actionCard;
				if (card != null)
				{
					playedCards.put(card, boardTile);
				}
			}

			// Get the cards that have been played by each player
			for each (var player:Player in game.players)
			{
				var playerCards:ListCollectionView = player.farm.playedCards;
				if (playerCards != null)
				{
					for each (var playerCard:Card in playerCards)
					{
						playedCards.put(playerCard, playedCards);
					}
				}
			}

			return playedCards;
		}

		private static function checkResource(player:Player, resource:String, resourceCost:int):Boolean
		{
			var result:Boolean = false;

			var resources:IMap = player.farm.resources;
			if (resources != null)
			{
				var stock:int = resources.getValue(resource);
				if (resourceCost <= stock)
				{
					result = true;
				}
			}
			if (resourceCost == 0)
			{
				result = true;
			}

			return result;
		}

		private static function hasImprovement(player:Player, improvementType:String):Boolean
		{
			var result:Boolean = false;
			var playedCards:ListCollectionView = player.farm.playedCards;
			for each (var card:Card in playedCards)
			{
				if (card is ImprovementCard)
				{
					var improvementCard:ImprovementCard = card as ImprovementCard;
					var types:ListCollectionView = improvementCard.types;
					if (types != null)
					{
						for each (var type:String in types)
						{
							if (improvementType == type)
							{
								result = true;
								break;
							}
						}
					}
				}
			}
			return result;
		}

		private static function haveAllPlayersPlayed(game:Game):Boolean
		{
			var result:Boolean = true;
			var players:ListCollectionView = game.players;
			for each (var player:Player in players)
			{
				var hasPlayedAllPeeps:Boolean = GameHelper.hasPlayedAllPeeps(player);
				result = result && hasPlayedAllPeeps;
			}
			return result;
		}

		private static function hasPlayedAllPeeps(player:Player):Boolean
		{
			var result:Boolean = false;
			var peepsOnFarm:ListCollectionView = GameHelper.getPeepsOnFarm(player.farm);
			if (peepsOnFarm == null || peepsOnFarm.length == 0)
			{
				result = true;
			}
			return result;
		}

		private static function getHarvestRounds():HashSet
		{
			var harvestRounds:HashSet = new HashSet();

			harvestRounds.add(4);
			harvestRounds.add(7);
			harvestRounds.add(9);
			harvestRounds.add(11);
			harvestRounds.add(13);
			harvestRounds.add(14);

			return harvestRounds;
		}

		private static function payResources(farm:Farm, resource:String, resourceCost:int):void
		{
			var resources:IMap = farm.resources;
			var stock:int = resources.getValue(resource);
			var newStock:int = stock - resourceCost;
			if (newStock < 0)
			{
				throw new Error("The player does not have enough resources to pay");
			}
//            else if (newStock == 0)
//            {
//                resources.remove(resource);
//            }
			else
			{
				resources.put(resource, newStock);
			}
		}
		
		public static function getPlayerById(game:Game, playerId:int):Player
		{
			var result:Player = null;
			
			for each (var player:Player in game.players)
			{
				if (player.id == playerId)
				{
					result = player;
					break;
				}
			}
			
			return result;
		}
		
		public static function getWinner(game:Game):Player
		{
			var tempBest:int = int.MIN_VALUE;
			var winner:Player = null;
			for each (var player:Player in game.players)
			{
				var playerTotal:int = player.score.total();
				if (playerTotal > tempBest)
				{
					tempBest = playerTotal;
					winner = player;
				}
			}
			return winner;
		}
	}
}